from conan import ConanFile
from conan.errors import ConanException, ConanInvalidConfiguration
from conan.tools.apple import is_apple_os, to_apple_arch, XCRun
from conan.tools.build import build_jobs, cross_building, valid_min_cppstd, supported_cppstd
from conan.tools.env import VirtualBuildEnv
from conan.tools.files import (
    apply_conandata_patches, chdir, collect_libs, copy, export_conandata_patches,
    get, mkdir, rename, replace_in_file, rm, rmdir, save
)
from conan.tools.gnu import AutotoolsToolchain
from conan.tools.layout import basic_layout
from conan.tools.microsoft import is_msvc, is_msvc_static_runtime, MSBuildToolchain, msvc_runtime_flag, VCVars
from conan.tools.scm import Version
from conan.tools.scm import Git
from conan.tools.files import patch

import glob
from io import StringIO
import os
import re
import shlex
import shutil
import sys
import yaml

required_conan_version = ">=1.53.0"

# When adding (or removing) an option, also add this option to the list in
# `rebuild-dependencies.yml` and re-run that script.
CONFIGURE_OPTIONS = (
    "atomic",
    "charconv",
    "chrono",
    "cobalt",
    "container",
    "context",
    "contract",
    "coroutine",
    "date_time",
    "exception",
    "fiber",
    "filesystem",
    "graph",
    "graph_parallel",
    "iostreams",
    "json",
    "locale",
    "log",
    "math",
    "mpi",
    "nowide",
    "program_options",
    "python",
    "random",
    "regex",
    "serialization",
    "stacktrace",
    "system",
    "test",
    "thread",
    "timer",
    "type_erasure",
    "url",
    "wave",
)


class BoostConan(ConanFile):
    name = "boost"
    version = "1.86.0"  # 添加版本号
    description = "Boost provides free peer-reviewed portable C++ source libraries"
    url = "https://github.com/conan-io/conan-center-index"
    homepage = "https://www.boost.org"
    license = "BSL-1.0"
    user = "thirdparty"
    channel = "stable"# stable development
    topics = ("libraries", "cpp")

    # 将补丁文件包括在构建过程中
    exports_sources = "1.82.0-locale-iconv-library-option.patch"

    settings = "os", "arch", "compiler", "build_type"
    options = {
        "shared": [True, False],
        "fPIC": [True, False],
        "header_only": [True, False],
        "error_code_header_only": [True, False],
        "system_no_deprecated": [True, False],
        "asio_no_deprecated": [True, False],
        "filesystem_no_deprecated": [True, False],
        "filesystem_use_std_fs": [True, False],
        "filesystem_version": [None, "3", "4"],
        "layout": ["system", "versioned", "tagged"],
        "magic_autolink": [True, False],  # enables BOOST_ALL_NO_LIB
        "diagnostic_definitions": [True, False],  # enables BOOST_LIB_DIAGNOSTIC
        "python_executable": [None, "ANY"],  # system default python installation is used, if None
        "python_version": [None, "ANY"],  # major.minor; computed automatically, if None
        "namespace": ["ANY"],  # custom boost namespace for bcp, e.g. myboost
        "namespace_alias": [True, False],  # enable namespace alias for bcp, boost=myboost
        "multithreading": [True, False],  # enables multithreading support
        "numa": [True, False],
        "zlib": [True, False],
        "bzip2": [True, False],
        "lzma": [True, False],
        "zstd": [True, False],
        "segmented_stacks": [True, False],
        "debug_level": list(range(0, 14)),
        "pch": [True, False],
        "extra_b2_flags": [None, "ANY"],  # custom b2 flags
        "i18n_backend": ["iconv", "icu", None, "deprecated"],
        "i18n_backend_iconv": ["libc", "libiconv", "off"],
        "i18n_backend_icu": [True, False],
        "visibility": ["global", "protected", "hidden"],
        "addr2line_location": ["ANY"],
        "with_stacktrace_backtrace": [True, False],
        "buildid": [None, "ANY"],
        "python_buildid": [None, "ANY"],
        "system_use_utf8": [True, False],
    }
    options.update({f"without_{_name}": [True, False] for _name in CONFIGURE_OPTIONS})

    default_options = {
        "shared": True,
        "fPIC": True,
        "header_only": False,
        "error_code_header_only": False,
        "system_no_deprecated": False,
        "asio_no_deprecated": False,
        "filesystem_no_deprecated": False,
        "filesystem_use_std_fs": False,
        "filesystem_version": None,
        "layout": "system",
        "magic_autolink": False,
        "diagnostic_definitions": False,
        "python_executable": None,
        "python_version": None,
        "namespace": "boost",
        "namespace_alias": False,
        "multithreading": True,
        "numa": True,
        "zlib": False,
        "bzip2": False,
        "lzma": False,
        "zstd": False,
        "segmented_stacks": False,
        "debug_level": 0,
        "pch": True,
        "extra_b2_flags": None,
        "i18n_backend": "deprecated",
        "i18n_backend_iconv": "libc",
        "i18n_backend_icu": False,
        "visibility": "hidden",
        "addr2line_location": "/usr/bin/addr2line",
        "with_stacktrace_backtrace": True,
        "buildid": None,
        "python_buildid": None,
        "system_use_utf8": False,
    }
    default_options.update({f"without_{_name}": False for _name in CONFIGURE_OPTIONS})

    default_options.update({
        "without_stacktrace": False,
        "without_thread": False,
        "without_serialization": True,
        "asio_no_deprecated": True,
        "system_no_deprecated": True,
        "without_atomic": False,
        "without_regex": True,
        "without_graph": True,
        "without_coroutine": True,
        "without_fiber": True,
        "without_math": True,
        "without_log": True,
        "without_test": True,
        "without_wave": True,
        "without_timer": True,
        "without_charconv": True,
        "without_json": True,
        "without_url": True,
        "without_type_erasure": True,
        "without_locale": True,
        "without_contract": True,
        "without_nowide": True,
        "without_filesystem": True,
        "without_program_options": False,
        "without_iostreams": True,
        "without_random": True,
        "without_context": True
    })


    default_options.update({f"without_{_name}": True for _name in ("graph_parallel", "mpi", "python")})

    short_paths = True
    no_copy_source = True
    _cached_dependencies = None

    def export(self):
        copy(self, f"dependencies/{self._dependency_filename}", src=self.recipe_folder, dst=self.export_folder)

    def export_sources(self):
        export_conandata_patches(self)

    def _cppstd_flag(self, compiler_cppstd=None):
        """Return the flag for the given C++ standard and compiler"""
        # TODO: Replace it by Conan tool when available: https://github.com/conan-io/conan/issues/12603
        compiler = self.settings.get_safe("compiler")
        compiler_version = self.settings.get_safe("compiler.version")
        cppstd = self.settings.get_safe("compiler.cppstd") or compiler_cppstd
        if not compiler or not compiler_version or not cppstd:
            return ""

        def _cppstd_gcc(gcc_version, cppstd):
            """Return the flag for the given C++ standard and GCC version"""
            cppstd_flags = {}
            cppstd_flags.setdefault("98", "98" if gcc_version >= "3.4" else None)
            cppstd_flags.setdefault("11", "11" if gcc_version >= "4.7" else "0x" if gcc_version >= "4.3" else None)
            cppstd_flags.setdefault("14", "14" if gcc_version >= "4.9" else "1y" if gcc_version >= "4.8" else None)
            cppstd_flags.setdefault("17", "17" if gcc_version >= "5.2" else "1z" if gcc_version >= "5" else None)
            cppstd_flags.setdefault("20", "2a" if gcc_version >= "8" else "20" if gcc_version >= "12" else None)
            cppstd_flags.setdefault("23", "2b" if gcc_version >= "11" else None)
            return cppstd_flags.get(cppstd.lstrip("gnu"))

        def _cppstd_clang(clang_version, cppstd):
            """Return the flag for the given C++ standard and Clang version"""
            cppstd_flags = {}
            cppstd_flags.setdefault("98", "98" if clang_version >= "2.1" else None)
            cppstd_flags.setdefault("11", "11" if clang_version >= "3.1" else "0x" if clang_version >= "2.1" else None)
            cppstd_flags.setdefault("14", "14" if clang_version >= "3.5" else "1y" if clang_version >= "3.4" else None)
            cppstd_flags.setdefault("17", "17" if clang_version >= "5" else "1z" if clang_version >= "3.5" else None)
            cppstd_flags.setdefault("20", "2a" if clang_version >= "6" else "20" if clang_version >= "12" else None)
            cppstd_flags.setdefault("23", "2b" if clang_version >= "13"  else "23" if clang_version >= "17" else None)
            return cppstd_flags.get(cppstd.lstrip("gnu"))


        def _cppstd_apple_clang(clang_version, cppstd):
            """Return the flag for the given C++ standard and Apple Clang version"""
            cppstd_flags = {}
            cppstd_flags.setdefault("98", "98" if clang_version >= "4.0" else None)
            cppstd_flags.setdefault("11", "11" if clang_version >= "4.0" else None)
            cppstd_flags.setdefault("14", "14" if clang_version >= "6.1" else "1y" if clang_version >= "5.1" else None)
            cppstd_flags.setdefault("17", "17" if clang_version >= "9.1" else "1z" if clang_version >= "6.1" else None)
            cppstd_flags.setdefault("20", "20" if clang_version >= "13.0" else "2a" if clang_version >= "10.0" else None)
            cppstd_flags.setdefault("23", "2b" if clang_version >= "13.0" else None)
            return cppstd_flags.get(cppstd.lstrip("gnu"))

        def _cppstd_msvc(visual_version, cppstd):
            """Return the flag for the given C++ standard and MSVC version"""
            cppstd_flags = {}
            cppstd_flags.setdefault("98", "98")
            cppstd_flags.setdefault("11", "11")
            cppstd_flags.setdefault("14", "14" if visual_version >= "190" else None)
            cppstd_flags.setdefault("17", "17" if visual_version >= "191" else "latest" if visual_version >= "190" else None)
            cppstd_flags.setdefault("20", "20" if visual_version >= "192" else "latest" if visual_version >= "191" else None)
            cppstd_flags.setdefault("23", "latest" if visual_version >= "193" else None)
            return cppstd_flags.get(cppstd)

        func = {"gcc": _cppstd_gcc, "clang": _cppstd_clang, "apple-clang": _cppstd_apple_clang, "msvc": _cppstd_msvc}.get(compiler)
        flag = cppstd
        if func:
            flag = func(Version(compiler_version), str(cppstd))
        return flag

    @property
    def _min_compiler_version_default_cxx11(self):
        """ Minimum compiler version having c++ standard >= 11
        """
        return {
            "gcc": 6,
            "clang": 6,
            "apple-clang": 99,  # still uses C++98 by default. XCode does not reflect apple-clang
            "Visual Studio": 14,  # guess
            "msvc": 190,  # guess
        }.get(str(self.settings.compiler))

    @property
    def _min_compiler_version_default_cxx14(self):
        """ Minimum compiler version having c++ standard >= 14
        https://gcc.gnu.org/gcc-6/changes.html
        https://releases.llvm.org/6.0.0/tools/clang/docs/ReleaseNotes.html#id9
        https://learn.microsoft.com/en-us/cpp/build/reference/std-specify-language-standard-version?view=msvc-150#remarks
        """
        return {
            "gcc": 6,
            "clang": 6,
            "apple-clang": 99,  # still uses C++98 by default. XCode does not reflect apple-clang
            "Visual Studio": 15,  # guess
            "msvc": 191,  # guess
        }.get(str(self.settings.compiler))

    @property
    def _min_compiler_version_default_cxx20(self):
        return {
            "gcc": 99,
            "clang": 99,
            "apple-clang": 99,
            "Visual Studio": 99,
            "msvc": 999,
        }.get(str(self.settings.compiler))

    @property
    def _has_cppstd_11_supported(self):
        cppstd = self.settings.compiler.get_safe("cppstd")
        if cppstd:
            return valid_min_cppstd(self, 11)
        compiler_version = self._min_compiler_version_default_cxx11
        if compiler_version:
            return (Version(self.settings.compiler.version) >= compiler_version) or "11" in supported_cppstd(self)

    @property
    def _has_cppstd_14_supported(self):
        cppstd = self.settings.compiler.get_safe("cppstd")
        if cppstd:
            return valid_min_cppstd(self, 14)
        required_compiler_version = self._min_compiler_version_default_cxx14
        if required_compiler_version:
            msvc_versions = {14: 190, 15: 191, 16: 192, 17: 193}
            compiler_version = Version(self.settings.compiler.version)
            is_visual_studio = str(self.settings.compiler) == "Visual Studio"
            # supported_cppstd only supports msvc, but not Visual Studio as compiler
            supported_cxx14 = "14" in supported_cppstd(self, "msvc", msvc_versions.get(compiler_version)) if is_visual_studio else "14" in supported_cppstd(self)
            # supported_cppstd: lists GCC 5 due partial support for C++14, but not enough for Boost
            return (compiler_version >= required_compiler_version) and supported_cxx14

    @property
    def _has_cppstd_20_supported(self):
        cppstd = self.settings.compiler.get_safe("cppstd")
        if cppstd:
            return valid_min_cppstd(self, 20)
        required_compiler_version = self._min_compiler_version_default_cxx20
        if required_compiler_version:
            msvc_versions = {14: 190, 15: 191, 16: 192, 17: 193}
            compiler_version = Version(self.settings.compiler.version)
            is_visual_studio = str(self.settings.compiler) == "Visual Studio"
            # supported_cppstd only supports msvc, but not Visual Studio as compiler
            supported_cxx20 = "20" in supported_cppstd(self, "msvc", msvc_versions.get(compiler_version)) if is_visual_studio else "20" in supported_cppstd(self)
            # We still dont have a compiler using C++20 by default
            return (compiler_version >= required_compiler_version) or supported_cxx20

    @property
    def _has_coroutine_supported(self):
        cppstd = self.settings.compiler.get_safe("cppstd")
        cppstd_20_supported = True
        if cppstd:
            cppstd_20_supported = valid_min_cppstd(self, 20)
        # https://en.cppreference.com/w/cpp/compiler_support#cpp20
        # https://releases.llvm.org/14.0.0/tools/clang/docs/ReleaseNotes.html#clang-format: before is experimental header
        # https://gcc.gnu.org/gcc-10/changes.html: requires -fcoroutines
        min_compiler_versions = {
                "apple-clang": "12",
                "clang": "14",
                "gcc": "10",
                "msvc": "192",
                "Visual Studio": "16",}
        required_compiler_version = min_compiler_versions.get(str(self.settings.compiler))
        if not required_compiler_version:
            return cppstd_20_supported
        return cppstd_20_supported and Version(self.settings.compiler.version) >= required_compiler_version

    @property
    def _min_compiler_version_nowide(self):
        # Nowide needs c++11 + swappable std::fstream
        return {
            "gcc": 4.8,
            "clang": 5,
            "Visual Studio": 14,  # guess
            "msvc": 190,  # guess
        }.get(str(self.settings.compiler))

    @property
    def _dependency_filename(self):
        return f"dependencies-{self.version}.yml"

    @property
    def _dependencies(self):
        if self._cached_dependencies is None:
            dependencies_filepath = os.path.join(self.recipe_folder, "dependencies", self._dependency_filename)
            if not os.path.isfile(dependencies_filepath):
                raise ConanException(f"Cannot find {dependencies_filepath}")
            with open(dependencies_filepath, encoding='utf-8') as f:
                self._cached_dependencies = yaml.safe_load(f)
        return self._cached_dependencies

    def _all_dependent_modules(self, name):
        dependencies = {name}
        while True:
            new_dependencies = set()
            for dependency in dependencies:
                new_dependencies.update(set(self._dependencies["dependencies"][dependency]))
                new_dependencies.update(dependencies)
            if len(new_dependencies) > len(dependencies):
                dependencies = new_dependencies
            else:
                break
        return dependencies

    def _all_super_modules(self, name):
        dependencies = {name}
        while True:
            new_dependencies = set(dependencies)
            for module in self._dependencies["dependencies"]:
                if dependencies.intersection(set(self._dependencies["dependencies"][module])):
                    new_dependencies.add(module)
            if len(new_dependencies) > len(dependencies):
                dependencies = new_dependencies
            else:
                break
        return dependencies

    @property
    def _bcp_dir(self):
        return "custom-boost"

    @property
    def _settings_build(self):
        return getattr(self, "settings_build", self.settings)

    @property
    def _is_clang_cl(self):
        return self.settings.os == "Windows" and self.settings.compiler == "clang"

    @property
    def _python_executable(self):
        """
        obtain full path to the python interpreter executable
        :return: path to the python interpreter executable, either set by option, or system default
        """
        exe = self.options.python_executable if self.options.python_executable else sys.executable
        return str(exe).replace("\\", "/")

    @property
    def _is_windows_platform(self):
        return self.settings.os in ["Windows", "WindowsStore", "WindowsCE"]

    @property
    def _is_apple_embedded_platform(self):
        return self.settings.os in ["iOS", "watchOS", "tvOS"]

    def config_options(self):
        if self.settings.os == "Windows":
            del self.options.fPIC

        # Test whether all config_options from the yml are available in CONFIGURE_OPTIONS
        for opt_name in self._configure_options:
            if f"without_{opt_name}" not in self.options:
                raise ConanException(f"{self._dependency_filename} has the configure options {opt_name} which is not available in conanfile.py")

        # stacktrace_backtrace not supported on Windows
        if self.settings.os == "Windows":
            del self.options.with_stacktrace_backtrace

        # nowide requires a c++11-able compiler + movable std::fstream: change default to not build on compiler with too old default c++ standard or too low compiler.cppstd
        # json requires a c++11-able compiler: change default to not build on compiler with too old default c++ standard or too low compiler.cppstd
        if self.settings.compiler.get_safe("cppstd"):
            if not valid_min_cppstd(self, 11):
                self.options.without_fiber = True
                self.options.without_nowide = True
                self.options.without_json = True
                self.options.without_url = True
        else:
            version_cxx11_standard_json = self._min_compiler_version_default_cxx11
            if version_cxx11_standard_json:
                if not self._has_cppstd_11_supported:
                    self.options.without_fiber = True
                    self.options.without_json = True
                    self.options.without_nowide = True
                    self.options.without_url = True
            else:
                self.options.without_fiber = True
                self.options.without_json = True
                self.options.without_nowide = True
                self.options.without_url = True
        if Version(self.version) >= "1.85.0" and not self._has_cppstd_14_supported:
            self.options.without_math = True
        if Version(self.version) >= "1.86.0" and not self._has_cppstd_14_supported:
            self.options.without_graph = True

        # iconv is off by default on Windows and Solaris
        if self._is_windows_platform or self.settings.os == "SunOS":
            self.options.i18n_backend_iconv = "off"
        elif is_apple_os(self):
            self.options.i18n_backend_iconv = "libiconv"
        elif self.settings.os == "Android":
            # bionic provides iconv since API level 28
            api_level = self.settings.get_safe("os.api_level")
            if api_level and Version(api_level) < "28":
                self.options.i18n_backend_iconv = "libiconv"

        # Remove options not supported by this version of boost
        for dep_name in CONFIGURE_OPTIONS:
            if dep_name not in self._configure_options:
                delattr(self.options, f"without_{dep_name}")

        def disable_math():
            super_modules = self._all_super_modules("math")
            for smod in super_modules:
                try:
                    setattr(self.options, f"without_{smod}", True)
                except ConanException:
                    pass

        def disable_graph():
            super_modules = self._all_super_modules("graph")
            for smod in super_modules:
                try:
                    setattr(self.options, f"without_{smod}", True)
                except ConanException:
                    pass

        if Version(self.version) >= "1.76.0":
            # Starting from 1.76.0, Boost.Math requires a c++11 capable compiler
            # ==> disable it by default for older compilers or c++ standards
            if self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 11):
                    disable_math()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx11
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++11 by default")
                elif not self._has_cppstd_11_supported:
                    disable_math()
                # Boost.Math is not built when the compiler is GCC < 5 and uses C++11
                elif self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "5":
                    disable_math()

        if Version(self.version) >= "1.79.0":
            # Starting from 1.79.0, Boost.Wave requires a c++11 capable compiler
            # ==> disable it by default for older compilers or c++ standards

            def disable_wave():
                super_modules = self._all_super_modules("wave")
                for smod in super_modules:
                    try:
                        setattr(self.options, f"without_{smod}", True)
                    except ConanException:
                        pass

            if self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 11):
                    disable_wave()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx11
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++11 by default")
                elif not self._has_cppstd_11_supported:
                    disable_wave()
                # Boost.Wave is not built when the compiler is GCC < 5 and uses C++11
                elif self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "5":
                    disable_wave()

        if Version(self.version) >= "1.81.0":
            # Starting from 1.81.0, Boost.Locale requires a c++11 capable compiler
            # ==> disable it by default for older compilers or c++ standards

            def disable_locale():
                super_modules = self._all_super_modules("locale")
                for smod in super_modules:
                    try:
                        setattr(self.options, f"without_{smod}", True)
                    except ConanException:
                        pass

            if self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 11):
                    disable_locale()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx11
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++11 by default")
                elif not self._has_cppstd_11_supported:
                    disable_locale()
                # Boost.Locale is not built when the compiler is GCC < 5 and uses C++11
                elif self.settings.compiler == "gcc" and Version(self.settings.compiler.version) < "5":
                    disable_locale()

        if Version(self.version) >= "1.84.0":
            # Starting from 1.84.0, Boost.Cobalt requires a c++20 capable compiler
            # ==> disable it by default for older compilers or c++ standards

            def disable_cobalt():
                super_modules = self._all_super_modules("cobalt")
                for smod in super_modules:
                    try:
                        setattr(self.options, f"without_{smod}", True)
                    except ConanException:
                        pass

            if not self._has_coroutine_supported:
                disable_cobalt()
            elif self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 20):
                    disable_cobalt()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx20
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++20 by default")
                elif Version(self.settings.compiler.version) < min_compiler_version:
                    disable_cobalt()

            # FIXME: Compilation errors on msvc shared build for boost.fiber https://github.com/boostorg/fiber/issues/314
            if is_msvc(self):
                self.options.without_fiber = True

        if Version(self.version) >= "1.85.0":
            # Starting from 1.85.0, Boost.Math requires a c++14 capable compiler
            # https://github.com/boostorg/math/blob/boost-1.85.0/README.md
            # ==> disable it by default for older compilers or c++ standards
            if self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 14):
                    disable_math()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx14
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++14 by default")
                elif not self._has_cppstd_14_supported:
                    disable_math()

        if Version(self.version) >= "1.86.0":
            # Boost 1.86.0 updated more components that require C++14 and C++17
            # https://www.boost.org/users/history/version_1_86_0.html
            if self.settings.compiler.get_safe("cppstd"):
                if not valid_min_cppstd(self, 14):
                    disable_graph()
            else:
                min_compiler_version = self._min_compiler_version_default_cxx14
                if min_compiler_version is None:
                    self.output.warning("Assuming the compiler supports c++14 by default")
                elif not self._has_cppstd_14_supported:
                    disable_graph()

    @property
    def _configure_options(self):
        return self._dependencies["configure_options"]

    @property
    def _fPIC(self):
        return self.options.get_safe("fPIC", self.default_options["fPIC"])

    @property
    def _shared(self):
        return self.options.get_safe("shared", self.default_options["shared"])

    @property
    def _stacktrace_addr2line_available(self):
        if (self._is_apple_embedded_platform or self.settings.get_safe("os.subsystem") == "catalyst"):
             # sandboxed environment - cannot launch external processes (like addr2line), system() function is forbidden
            return False
        return not self.options.header_only and not self.options.without_stacktrace and self.settings.os != "Windows"

    @property
    def _stacktrace_from_exception_available(self):
        if Version(self.version) == "1.85.0":
            # https://github.com/boostorg/stacktrace/blob/boost-1.85.0/build/Jamfile.v2#L143
            return not self.options.header_only and not self.options.without_stacktrace and self.settings.os != "Windows"
        elif Version(self.version) >= "1.86.0":
            # https://github.com/boostorg/stacktrace/blob/boost-1.86.0/build/Jamfile.v2#L148
            return not self.options.header_only and not self.options.without_stacktrace and self.settings.arch == "x86_64"

    def configure(self):
        if self.options.header_only:
            self.options.rm_safe("shared")
            self.options.rm_safe("fPIC")
        elif self.options.shared:
            self.options.rm_safe("fPIC")

        if self.options.i18n_backend != "deprecated":
            self.output.warning("i18n_backend option is deprecated, do not use anymore.")
            if self.options.i18n_backend == "iconv":
                self.options.i18n_backend_iconv = "libiconv"
                self.options.i18n_backend_icu = False
            if self.options.i18n_backend == "icu":
                self.options.i18n_backend_iconv = "off"
                self.options.i18n_backend_icu = True
            if self.options.i18n_backend == "None":
                self.options.i18n_backend_iconv = "off"
                self.options.i18n_backend_icu = False
        if self.options.without_locale:
            self.options.rm_safe("i18n_backend_iconv")
            self.options.rm_safe("i18n_backend_icu")

        if not self.options.without_python:
            if not self.options.python_version:
                self.options.python_version = self._detect_python_version()
                self.options.python_executable = self._python_executable
        else:
            self.options.rm_safe("python_buildid")

        if not self._stacktrace_addr2line_available:
            self.options.rm_safe("addr2line_location")

        if self.options.get_safe("without_stacktrace", True):
            self.options.rm_safe("with_stacktrace_backtrace")

        if self.options.without_fiber:
            self.options.rm_safe("numa")

        # Use verbosity from [conf] if specified
        verbosity = self.conf.get("tools.build:verbosity", default="quiet")
        if verbosity == "verbose" and int(self.options.debug_level) < 2:
            self.options.debug_level.value = 2

    def layout(self):
        basic_layout(self, src_folder="src")

    @property
    def _cxx11_boost_libraries(self):
        libraries = ["fiber", "json", "nowide", "url"]
        if Version(self.version) >= "1.76.0":
            libraries.append("math")
        if Version(self.version) >= "1.79.0":
            libraries.append("wave")
        if Version(self.version) >= "1.81.0":
            libraries.append("locale")
        if Version(self.version) >= "1.84.0":
            libraries.append("atomic")
            libraries.append("filesystem")
            libraries.append("log")
            libraries.append("random")
            libraries.append("stacktrace")
            libraries.append("test")
            libraries.append("thread")
        if Version(self.version) >= "1.85.0":
            libraries.append("system")
        libraries.sort()
        return list(filter(lambda library: f"without_{library}" in self.options, libraries))

    @property
    def _cxx14_boost_libraries(self):
        libraries = []
        if Version(self.version) >= "1.85.0":
            # https://github.com/boostorg/math/blob/develop/README.md#boost-math-library
            libraries.append("math")
        libraries.sort()
        return list(filter(lambda library: f"without_{library}" in self.options, libraries))

    @property
    def _cxx20_boost_libraries(self):
        libraries = []
        if Version(self.version) >= "1.84.0":
            # https://github.com/boostorg/cobalt/blob/boost-1.84.0/build/Jamfile#L54
            libraries.append("cobalt")
        libraries.sort()
        return list(filter(lambda library: f"without_{library}" in self.options, libraries))

    def validate(self):
        if not self.options.multithreading:
            # * For the reason 'thread' is deactivate look at https://stackoverflow.com/a/20991533
            #   Look also on the comments of the answer for more details
            # * Although the 'context' and 'atomic' library does not mention anything about threading,
            #   when being build the compiler uses the -pthread flag, which makes it quite dangerous
            for lib in ["locale", "coroutine", "wave", "type_erasure", "fiber", "thread", "context", "atomic"]:
                if not self.options.get_safe(f"without_{lib}"):
                    raise ConanInvalidConfiguration(f"Boost '{lib}' library requires multi threading")

        if is_msvc(self) and self._shared and is_msvc_static_runtime(self):
            raise ConanInvalidConfiguration("Boost can not be built as shared library with MT runtime.")

        # FIXME: In 1.84.0, there are compilation errors on msvc shared build for boost.fiber. https://github.com/boostorg/fiber/issues/314
        if Version(self.version) >= "1.84.0" and is_msvc(self) and self._shared and not self.options.without_fiber:
            raise ConanInvalidConfiguration("Boost.fiber can not be built as shared library on MSVC.")

        if not self.options.without_locale and self.options.i18n_backend_iconv == "off" and \
           not self.options.i18n_backend_icu and not self._is_windows_platform:
            raise ConanInvalidConfiguration(
                "Boost.Locale library needs either iconv or ICU library to be built on non windows platforms"
            )

        if self._stacktrace_addr2line_available:
            if not os.path.isabs(str(self.options.addr2line_location)):
                raise ConanInvalidConfiguration("addr2line_location must be an absolute path to addr2line")

        # Check, when a boost module is enabled, whether the boost modules it depends on are enabled as well.
        for mod_name, mod_deps in self._dependencies["dependencies"].items():
            if not self.options.get_safe(f"without_{mod_name}", True):
                for mod_dep in mod_deps:
                    if self.options.get_safe(f"without_{mod_dep}", False):
                        raise ConanInvalidConfiguration(f"{mod_name} requires {mod_deps}: {mod_dep} is disabled")

        if not self.options.get_safe("without_nowide", True):
            # nowide require a c++11-able compiler with movable std::fstream
            mincompiler_version = self._min_compiler_version_nowide
            if mincompiler_version and Version(self.settings.compiler.version) < mincompiler_version:
                raise ConanInvalidConfiguration("This compiler is too old to build Boost.nowide.")

        for cxx_standard, boost_libraries, has_cppstd_supported in [
             (11, self._cxx11_boost_libraries, self._has_cppstd_11_supported),
             (14, self._cxx14_boost_libraries, self._has_cppstd_14_supported),
             (20, self._cxx20_boost_libraries, self._has_cppstd_20_supported)]:
            if any([not self.options.get_safe(f"without_{library}", True) for library in boost_libraries]):
                if (self.settings.compiler.get_safe("cppstd") and not valid_min_cppstd(self, cxx_standard)) or \
                    not has_cppstd_supported:
                    raise ConanInvalidConfiguration(
                        f"Boost libraries {', '.join(boost_libraries)} requires a C++{cxx_standard} compiler. "
                        "Please, set compiler.cppstd or use a newer compiler version or disable from building."
                    )
        if not self.options.get_safe("without_cobalt", True) and not self._has_coroutine_supported:
            raise ConanInvalidConfiguration("Boost.Cobalt requires a C++20 capable compiler. "
                                            "Please, set compiler.cppstd and use a newer compiler version, or disable from building.")

    def _with_dependency(self, dependency):
        """
        Return true when dependency is required according to the dependencies-x.y.z.yml file
        """
        for name, reqs in self._dependencies["requirements"].items():
            if dependency in reqs:
                if not self.options.get_safe(f"without_{name}", True):
                    return True
        return False

    @property
    def _with_zlib(self):
        return not self.options.header_only and self._with_dependency("zlib") and self.options.zlib

    @property
    def _with_bzip2(self):
        return not self.options.header_only and self._with_dependency("bzip2") and self.options.bzip2

    @property
    def _with_lzma(self):
        return not self.options.header_only and self._with_dependency("lzma") and self.options.lzma

    @property
    def _with_zstd(self):
        return not self.options.header_only and self._with_dependency("zstd") and self.options.zstd

    @property
    def _with_icu(self):
        return not self.options.header_only and self._with_dependency("icu") and self.options.get_safe("i18n_backend_icu")

    @property
    def _with_iconv(self):
        return not self.options.header_only and self._with_dependency("iconv") and self.options.get_safe("i18n_backend_iconv") == "libiconv"

    @property
    def _with_stacktrace_backtrace(self):
        return not self.options.header_only and self.options.get_safe("with_stacktrace_backtrace", False)

    def requirements(self):
        if self._with_zlib:
            self.requires("zlib/[>=1.2.11 <2]")
        if self._with_bzip2:
            self.requires("bzip2/1.0.8")
        if self._with_lzma:
            self.requires("xz_utils/[>=5.4.5 <6]")
        if self._with_zstd:
            self.requires("zstd/[>=1.5 <1.6]")
        if self._with_stacktrace_backtrace:
            self.requires("libbacktrace/cci.20240918@thirdparty/stable", transitive_headers=True, transitive_libs=True)

        if self._with_icu:
            self.requires("icu/74.2")
        if self._with_iconv:
            self.requires("libiconv/1.17")

    def package_id(self):
        del self.info.options.i18n_backend

        if self.info.options.header_only:
            self.info.clear()
        else:
            del self.info.options.debug_level
            del self.info.options.filesystem_version
            del self.info.options.pch
            del self.info.options.python_executable  # PATH to the interpreter is not important, only version matters
            if self.info.options.without_python:
                del self.info.options.python_version

    def build_requirements(self):
        if not self.options.header_only:
            self.tool_requires("b2/5.2.1@tools/stable")

    def source(self):# two step

        # Step 1: Clone the repository
        git = Git(self)
        git.clone("ssh://git@172.16.57.28:2289/ads/thirdparty/boost.git", target=self.source_folder)

        # Step 2: Check if the .git folder exists to ensure the repository is cloned correctly
        if not os.path.exists(os.path.join(self.source_folder, ".git")):
            raise ConanException("Git repository was not cloned successfully.")

        # Step 3: Switch to the desired branch (or tag) after cloning
        with chdir(self, self.source_folder):  # Ensure we're in the correct directory
            git.checkout("1.86.0")

        self.output.info("source ：self.recipe_folder  = " + self.recipe_folder)
        
        # # Step 4: Apply the patch manually
        # patch_file_path = os.path.join(self.source_folder, "../1.82.0-locale-iconv-library-option.patch")
        
        # if not os.path.exists(patch_file_path):
        #     raise ConanException(f"Patch file {patch_file_path} does not exist.")
        
        # # Apply the patch manually
        # self.output.info(f"Applying patch from {patch_file_path}")
        # patch(self, base_path=self.source_folder, patch_file=patch_file_path)



    def generate(self):
        if not self.options.header_only:
            env = VirtualBuildEnv(self)
            env.generate()
            vc = VCVars(self)
            vc.generate()

    ##################### BUILDING METHODS ###########################

    def _run_python_script(self, script):
        """
        execute python one-liner script and return its output
        :param script: string containing python script to be executed
        :return: output of the python script execution, or None, if script has failed
        """
        output = StringIO()
        command = f'"{self._python_executable}" -c "{script}"'
        self.output.info(f"running {command}")
        try:
            self.run(command, output, scope="run")
        except ConanException:
            self.output.info("(failed)")
            return None
        output = output.getvalue()
        # Conan is broken when run_to_output = True
        if "\n-----------------\n" in output:
            output = output.split("\n-----------------\n", 1)[1]
        output = output.strip()
        return output if output != "None" else None

    def _get_python_path(self, name):
        """
        obtain path entry for the python installation
        :param name: name of the python config entry for path to be queried (such as "include", "platinclude", etc.)
        :return: path entry from the sysconfig
        """
        # https://docs.python.org/3/library/sysconfig.html
        # https://docs.python.org/2.7/library/sysconfig.html
        return self._run_python_script("from __future__ import print_function; "
                                       "import sysconfig; "
                                       f"print(sysconfig.get_path('{name}'))")

    def _get_python_sc_var(self, name):
        """
        obtain value of python sysconfig variable
        :param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
        :return: value of python sysconfig variable
        """
        return self._run_python_script("from __future__ import print_function; "
                                       "import sysconfig; "
                                       f"print(sysconfig.get_config_var('{name}'))")

    def _get_python_du_var(self, name):
        """
        obtain value of python distutils sysconfig variable
        (sometimes sysconfig returns empty values, while python.sysconfig provides correct values)
        :param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
        :return: value of python sysconfig variable
        """
        return self._run_python_script("from __future__ import print_function; "
                                       "import distutils.sysconfig as du_sysconfig; "
                                       f"print(du_sysconfig.get_config_var('{name}'))")

    def _get_python_var(self, name):
        """
        obtain value of python variable, either by sysconfig, or by distutils.sysconfig
        :param name: name of variable to be queried (such as LIBRARY or LDLIBRARY)
        :return: value of python sysconfig variable

        NOTE: distutils is deprecated and breaks the recipe since Python 3.10
        """
        python_version_parts = str(self.info.options.python_version).split('.')
        python_major = int(python_version_parts[0])
        python_minor = int(python_version_parts[1])
        if(python_major >= 3 and python_minor >= 10):
            return self._get_python_sc_var(name)

        return self._get_python_sc_var(name) or self._get_python_du_var(name)

    def _detect_python_version(self):
        """
        obtain version of python interpreter
        :return: python interpreter version, in format major.minor
        """
        return self._run_python_script("from __future__ import print_function; "
                                       "import sys; "
                                       "print('{}.{}'.format(sys.version_info[0], sys.version_info[1]))")

    @property
    def _python_version(self):
        version = self._detect_python_version()
        if self.options.python_version and version != self.options.python_version:
            raise ConanInvalidConfiguration(f"detected python version {version} doesn't match conan option {self.options.python_version}")
        return version

    @property
    def _python_inc(self):
        """
        obtain the result of the "sysconfig.get_python_inc()" call
        :return: result of the "sysconfig.get_python_inc()" execution
        """
        return self._run_python_script("from __future__ import print_function; "
                                       "import sysconfig; "
                                       "print(sysconfig.get_python_inc())")

    @property
    def _python_abiflags(self):
        """
        obtain python ABI flags, see https://www.python.org/dev/peps/pep-3149/ for the details
        :return: the value of python ABI flags
        """
        return self._run_python_script("from __future__ import print_function; "
                                       "import sys; "
                                       "print(getattr(sys, 'abiflags', ''))")

    @property
    def _python_includes(self):
        """
        attempt to find directory containing Python.h header file
        :return: the directory with python includes
        """
        include = self._get_python_path("include")
        plat_include = self._get_python_path("platinclude")
        include_py = self._get_python_var("INCLUDEPY")
        include_dir = self._get_python_var("INCLUDEDIR")
        python_inc = self._python_inc

        candidates = [include,
                      plat_include,
                      include_py,
                      include_dir,
                      python_inc]
        for candidate in candidates:
            if candidate:
                python_h = os.path.join(candidate, 'Python.h')
                self.output.info(f"checking {python_h}")
                if os.path.isfile(python_h):
                    self.output.info(f"found Python.h: {python_h}")
                    return candidate.replace("\\", "/")
        raise Exception("couldn't locate Python.h - make sure you have installed python development files")

    @property
    def _python_library_dir(self):
        """
        attempt to find python development library
        :return: the full path to the python library to be linked with
        """
        library = self._get_python_var("LIBRARY")
        ldlibrary = self._get_python_var("LDLIBRARY")
        libdir = self._get_python_var("LIBDIR")
        multiarch = self._get_python_var("MULTIARCH")
        masd = self._get_python_var("multiarchsubdir")
        with_dyld = self._get_python_var("WITH_DYLD")
        if libdir and multiarch and masd and not libdir.endswith(masd):
            if masd.startswith(os.sep):
                masd = masd[len(os.sep):]
            self.output.warning(f"Python libdir candidate thingy: {libdir}")
            libdir = os.path.join(libdir, masd)

        if not libdir:
            libdest = self._get_python_var("LIBDEST")
            libdir = os.path.join(os.path.dirname(libdest), "libs")

        candidates = [ldlibrary, library]
        library_prefixes = [""] if is_msvc(self) else ["", "lib"]
        library_suffixes = [".lib"] if is_msvc(self) else [".so", ".dll.a", ".a"]
        if with_dyld:
            library_suffixes.insert(0, ".dylib")

        python_version = self._python_version
        python_version_no_dot = python_version.replace(".", "")
        versions = ["", python_version, python_version_no_dot]
        abiflags = self._python_abiflags

        for prefix in library_prefixes:
            for suffix in library_suffixes:
                for version in versions:
                    candidates.append(f"{prefix}python{version}{abiflags}{suffix}")

        for candidate in candidates:
            if candidate:
                python_lib = os.path.join(libdir, candidate)
                self.output.info(f"checking {python_lib}")
                if os.path.isfile(python_lib):
                    self.output.info(f"found python library: {python_lib}")
                    return libdir.replace("\\", "/")
        raise ConanInvalidConfiguration("couldn't locate python libraries - make sure you have installed python development files")

    def _clean(self):
        clean_dirs = [
            os.path.join(self.build_folder, "bin.v2"),
            os.path.join(self.build_folder, "architecture"),
            os.path.join(self.source_folder, self._bcp_dir),
            os.path.join(self.source_folder, "dist", "bin"),
            os.path.join(self.source_folder, "stage"),
            os.path.join(self.source_folder, "tools", "build", "src", "engine", "bootstrap"),
            os.path.join(self.source_folder, "tools", "build", "src", "engine", "bin.ntx86"),
            os.path.join(self.source_folder, "tools", "build", "src", "engine", "bin.ntx86_64"),
        ]
        for d in clean_dirs:
            if os.path.isdir(d):
                self.output.warning(f"removing '{d}'")
                shutil.rmtree(d)

    @property
    def _b2_exe(self):
        return "b2"

    @property
    def _bcp_exe(self):
        folder = os.path.join(self.source_folder, "dist", "bin")
        return os.path.join(folder, "bcp")

    @property
    def _use_bcp(self):
        return self.options.namespace != "boost"

    @property
    def _boost_build_dir(self):
        return os.path.join(self.source_folder, "tools", "build")

    def _build_bcp(self):
        folder = os.path.join(self.source_folder, "tools", "bcp")
        with chdir(self, folder):
            # 构建 b2 命令基础部分，增加调试输出级别
            command = f"{self._b2_exe} -j{build_jobs(self)} --abbreviate-paths toolset={self._toolset} -d+4"
            
            # 增加调试级别选项
            command += f" -d{self.options.debug_level}"

            # 获取 libbacktrace 的包含路径和库路径
            libbacktrace_include = self.deps_cpp_info["libbacktrace"].include_paths[0]
            libbacktrace_lib = self.deps_cpp_info["libbacktrace"].lib_paths[0]

            # 从 profile 获取环境变量
            sysroot_path = os.getenv("SYSROOT", "")
            cxxflags = os.getenv("CXXFLAGS", "")
            linkflags = os.getenv("LDFLAGS", "")

            # 打印获取到的环境变量
            self.output.info(f"SYSROOT: {sysroot_path}")
            self.output.info(f"CXXFLAGS: {cxxflags}")
            self.output.info(f"LDFLAGS: {linkflags}")

            # 如果 sysroot 被定义，则将其添加到命令中
            if sysroot_path:
                command += f" --sysroot={sysroot_path}"

            # 强制传递 libbacktrace 的包含路径和库路径
            command += f" cxxflags='-I{libbacktrace_include} -fPIC {cxxflags}'"
            command += f" linkflags='-L{libbacktrace_lib} -lbacktrace {linkflags}'"

            # 打印构建命令以便调试
            self.output.warning(f"Constructed build command: {command}")

            # 执行 b2 构建命令
            self.run(command)




    def _run_bcp(self):
        with chdir(self, self.source_folder):
            mkdir(self, self._bcp_dir)
            namespace = f"--namespace={self.options.namespace}"
            alias = "--namespace-alias" if self.options.namespace_alias else ""
            boostdir = f"--boost={self.source_folder}"
            libraries = {"build", "boost-build.jam", "boostcpp.jam", "boost_install", "headers"}
            for d in os.listdir(os.path.join(self.source_folder, "boost")):
                if os.path.isdir(os.path.join(self.source_folder, "boost", d)):
                    libraries.add(d)
            for d in os.listdir(os.path.join(self.source_folder, "libs")):
                if os.path.isdir(os.path.join(self.source_folder, "libs", d)):
                    libraries.add(d)
            libraries = " ".join(libraries)
            command = f"{self._bcp_exe} {namespace} {alias} {boostdir} {libraries} {self._bcp_dir}"
            self.output.warning(command)
            self.run(command)

    def build(self):
        stacktrace_jamfile = os.path.join(self.source_folder, "libs", "stacktrace", "build", "Jamfile.v2")
        if cross_building(self, skip_x64_x86=True):
            # When cross building, do not attempt to run the test-executable (assume they work)
            replace_in_file(self, stacktrace_jamfile, "$(>) > $(<)", "echo \"\" > $(<)", strict=False)
        if self._with_stacktrace_backtrace and self.settings.os != "Windows":
            # 检查 libbacktrace 是否可用并打印库路径
            try:
                libbacktrace_libdir = self.dependencies["libbacktrace"].cpp_info.aggregated_components().libdirs[0]
                self.output.info(f"Libbacktrace library directory: {libbacktrace_libdir}")
            except KeyError:
                self.output.error("libbacktrace dependency not found. Aborting.")
                raise ConanInvalidConfiguration("libbacktrace not found, build cannot proceed.")
            
            # 设置库的链接路径并修改 Jamfile
            linker_var = "DYLD_LIBRARY_PATH" if self.settings.os == "Macos" else "LD_LIBRARY_PATH"
            patched_run_rule = f"{linker_var}={libbacktrace_libdir} $(>) > $(<)"
            self.output.info(f"Patched run rule: {patched_run_rule}")
            replace_in_file(self, stacktrace_jamfile, "$(>) > $(<)", patched_run_rule, strict=False)
            
            # 如果是共享库，替换链接类型
            if self.dependencies["libbacktrace"].options.shared:
                self.output.info("libbacktrace is shared, changing link type to shared.")
                replace_in_file(self, stacktrace_jamfile, "<link>static", "<link>shared", strict=False)
            else:
                self.output.warning("libbacktrace is not shared, skipping link type modification.")

        # Older clang releases require a thread_local variable to be initialized by a constant value
        replace_in_file(self, os.path.join(self.source_folder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
                              "/* thread_local */", "thread_local", strict=False)
        replace_in_file(self, os.path.join(self.source_folder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
                              "/* static __thread */", "static __thread", strict=False)
        if self.settings.compiler == "apple-clang" or (self.settings.compiler == "clang" and Version(self.settings.compiler.version) < 6):
            replace_in_file(self, os.path.join(self.source_folder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
                                  "thread_local", "/* thread_local */")
            replace_in_file(self, os.path.join(self.source_folder, "boost", "stacktrace", "detail", "libbacktrace_impls.hpp"),
                                  "static __thread", "/* static __thread */")
        replace_in_file(self, os.path.join(self.source_folder, "tools", "build", "src", "tools", "gcc.jam"),
                              "local generic-os = [ set.difference $(all-os) : aix darwin vxworks solaris osf hpux ] ;",
                              "local generic-os = [ set.difference $(all-os) : aix darwin vxworks solaris osf hpux iphone appletv ] ;",
                              strict=False)
        replace_in_file(self, os.path.join(self.source_folder, "tools", "build", "src", "tools", "gcc.jam"),
                              "local no-threading = android beos haiku sgi darwin vxworks ;",
                              "local no-threading = android beos haiku sgi darwin vxworks iphone appletv ;",
                              strict=False)
        replace_in_file(self, os.path.join(self.source_folder, "libs", "fiber", "build", "Jamfile.v2"),
                              "    <conditional>@numa",
                              "    <link>shared:<library>.//boost_fiber : <conditional>@numa",
                              strict=False)
        if self.settings.os == "Android":
            # force versionless soname from boostorg/boost#206
            # this can be applied to all versions and it's easier with a replace
            replace_in_file(self, os.path.join(self.source_folder, "boostcpp.jam"),
                            "! [ $(property-set).get <target-os> ] in windows cygwin darwin aix &&",
                            "! [ $(property-set).get <target-os> ] in windows cygwin darwin aix android &&",
                            strict=False)

        if self.options.header_only:
            self.output.warning("Header only package, skipping build")
            return

        self._clean()

        if self._use_bcp:
            self._build_bcp()
            self._run_bcp()

        self._create_user_config_jam(self._boost_build_dir)

        # JOIN ALL FLAGS
        b2_flags = " ".join(self._build_flags)
        full_command = f"{self._b2_exe} {b2_flags}"
        # -d2 is to print more debug info and avoid travis timing out without output
        sources = os.path.join(self.source_folder, self._bcp_dir) if self._use_bcp else self.source_folder
        full_command += f' --debug-configuration --build-dir="{self.build_folder}"'

        self.output.warning(f"self._use_bcp: {self._use_bcp}")
        self.output.warning(f"full_command: {full_command}")
        self.output.warning(full_command)

        # If sending a user-specified toolset to B2, setting the vcvars
        # interferes with the compiler selection.
        with chdir(self, sources):
            # To show the libraries *1
            # self.run("%s --show-libraries" % b2_exe)
            self.run(full_command)

    @property
    def _b2_os(self):
        return {
            "Windows": "windows",
            "WindowsStore": "windows",
            "Linux": "linux",
            "Android": "android",
            "Macos": "darwin",
            "iOS": "iphone",
            "watchOS": "iphone",
            "tvOS": "appletv",
            "FreeBSD": "freebsd",
            "SunOS": "solaris",
        }.get(str(self.settings.os))

    @property
    def _b2_address_model(self):
        if self.settings.arch in ("x86_64", "ppc64", "ppc64le", "mips64", "armv8", "armv8.3", "sparcv9"):
            return "64"

        return "32"

    @property
    def _b2_binary_format(self):
        return {
            "Windows": "pe",
            "WindowsStore": "pe",
            "Linux": "elf",
            "Android": "elf",
            "Macos": "mach-o",
            "iOS": "mach-o",
            "watchOS": "mach-o",
            "tvOS": "mach-o",
            "FreeBSD": "elf",
            "SunOS": "elf",
        }.get(str(self.settings.os))

    @property
    def _b2_architecture(self):
        if str(self.settings.arch).startswith("x86"):
            return "x86"
        if str(self.settings.arch).startswith("ppc"):
            return "power"
        if str(self.settings.arch).startswith("arm"):
            return "arm"
        if str(self.settings.arch).startswith("sparc"):
            return "sparc"
        if str(self.settings.arch).startswith("mips64"):
            return "mips64"
        if str(self.settings.arch).startswith("mips"):
            return "mips1"
        if str(self.settings.arch).startswith("s390"):
            return "s390x"

        return None

    @property
    def _b2_abi(self):
        if str(self.settings.arch).startswith("x86"):
            return "ms" if str(self.settings.os) in ["Windows", "WindowsStore"] else "sysv"
        if str(self.settings.arch).startswith("ppc"):
            return "sysv"
        if str(self.settings.arch).startswith("arm"):
            return "aapcs"
        if str(self.settings.arch).startswith("mips"):
            return "o32"

        return None

    @property
    def _gnu_cxx11_abi(self):
        """Checks libcxx setting and returns value for the GNU C++11 ABI flag
        _GLIBCXX_USE_CXX11_ABI= .  Returns None if C++ library cannot be
        determined.
        """
        try:
            if str(self.settings.compiler.libcxx) == "libstdc++":
                return "0"
            if str(self.settings.compiler.libcxx) == "libstdc++11":
                return "1"
        except ConanException:
            pass
        return None

    @property
    def _build_flags(self):
        flags = self._build_cross_flags

        # Stop at the first error. No need to continue building.
        flags.append("-q")

        if self.options.get_safe("numa"):
            flags.append("numa=on")

        # https://www.boost.org/doc/libs/1_70_0/libs/context/doc/html/context/architectures.html
        if not self._is_apple_embedded_platform and self._b2_os:
            flags.append(f"target-os={self._b2_os}")
        if self._b2_architecture:
            flags.append(f"architecture={self._b2_architecture}")
        if self._b2_address_model:
            flags.append(f"address-model={self._b2_address_model}")
        if self._b2_binary_format:
            flags.append(f"binary-format={self._b2_binary_format}")
        if self._b2_abi:
            flags.append(f"abi={self._b2_abi}")

        flags.append(f"--layout={self.options.layout}")
        flags.append(f"--user-config={os.path.join(self._boost_build_dir, 'user-config.jam')}")
        flags.append(f"-sNO_ZLIB={'0' if self._with_zlib else '1'}")
        flags.append(f"-sNO_BZIP2={'0' if self._with_bzip2 else '1'}")
        flags.append(f"-sNO_LZMA={'0' if self._with_lzma else '1'}")
        flags.append(f"-sNO_ZSTD={'0' if self._with_zstd else '1'}")

        if self.options.get_safe("i18n_backend_icu"):
            flags.append("boost.locale.icu=on")
        else:
            flags.append("boost.locale.icu=off")
            flags.append("--disable-icu")
        if self.options.get_safe("i18n_backend_iconv") in ["libc", "libiconv"]:
            flags.append("boost.locale.iconv=on")
            if self.options.get_safe("i18n_backend_iconv") == "libc":
                flags.append("boost.locale.iconv.lib=libc")
            else:
                flags.append("boost.locale.iconv.lib=libiconv")
        else:
            flags.append("boost.locale.iconv=off")
            flags.append("--disable-iconv")

        def add_defines(library):
            for define in self.dependencies[library].cpp_info.aggregated_components().defines:
                flags.append(f"define={define}")

        if self._with_zlib:
            add_defines("zlib")
        if self._with_bzip2:
            add_defines("bzip2")
        if self._with_lzma:
            add_defines("xz_utils")
        if self._with_zstd:
            add_defines("zstd")

        for define in self.conf.get("tools.build:defines", default=[], check_type=list):
            flags.append(f"define={define}")

        if is_msvc(self):
            flags.append(f"runtime-link={'static' if is_msvc_static_runtime(self) else 'shared'}")
            flags.append(f"runtime-debugging={'on' if 'd' in msvc_runtime_flag(self) else 'off'}")

        # For details https://boostorg.github.io/build/manual/master/index.html
        flags.append(f"threading={'single' if not self.options.multithreading else 'multi'}")
        flags.append(f"visibility={self.options.visibility}")

        flags.append(f"link={'shared' if self._shared else 'static'}")
        if self.settings.build_type == "Debug":
            flags.append("variant=debug")
        else:
            flags.append("variant=release")

        for libname in self._configure_options:
            if not getattr(self.options, f"without_{libname}"):
                flags.append(f"--with-{libname}")

        flags.append(f"toolset={self._toolset}")

        safe_cppstd = self.settings.get_safe("compiler.cppstd")
        if safe_cppstd:
            cppstd_version = self._cppstd_flag(safe_cppstd)
            flags.append(f"cxxstd={cppstd_version}")
            if "gnu" in safe_cppstd:
                flags.append("cxxstd-dialect=gnu")
        elif Version(self.version) >= "1.85.0" and self._has_cppstd_14_supported:
            cppstd_version = self._cppstd_flag("14")
            flags.append(f"cxxstd={cppstd_version}")
        elif self._has_cppstd_11_supported:
            cppstd_version = self._cppstd_flag("11")
            flags.append(f"cxxstd={cppstd_version}")

        # LDFLAGS
        link_flags = []

        # CXX FLAGS
        cxx_flags = []
        # fPIC DEFINITION
        if self._fPIC:
            cxx_flags.append("-fPIC")
        if self.settings.build_type == "RelWithDebInfo":
            if self.settings.compiler == "gcc" or "clang" in str(self.settings.compiler):
                cxx_flags.append("-g")
            elif is_msvc(self):
                cxx_flags.append("/Z7")

        # Standalone toolchain fails when declare the std lib
        if self.settings.os not in ("Android", "Emscripten"):
            try:
                if self._gnu_cxx11_abi:
                    flags.append(f"define=_GLIBCXX_USE_CXX11_ABI={self._gnu_cxx11_abi}")

                if self.settings.compiler in ("clang", "apple-clang"):
                    libcxx = {
                        "libstdc++11": "libstdc++",
                    }.get(str(self.settings.compiler.libcxx), str(self.settings.compiler.libcxx))
                    cxx_flags.append(f"-stdlib={libcxx}")
                    link_flags.append(f"-stdlib={libcxx}")
            except ConanException:
                pass

        if self.options.error_code_header_only:
            flags.append("define=BOOST_ERROR_CODE_HEADER_ONLY=1")
        if self.options.system_no_deprecated:
            flags.append("define=BOOST_SYSTEM_NO_DEPRECATED=1")
        if self.options.asio_no_deprecated:
            flags.append("define=BOOST_ASIO_NO_DEPRECATED=1")
        if self.options.filesystem_no_deprecated:
            flags.append("define=BOOST_FILESYSTEM_NO_DEPRECATED=1")
        if self.options.filesystem_use_std_fs:
            flags.append("define=BOOST_DLL_USE_STD_FS=1")
        if self.options.system_use_utf8:
            flags.append("define=BOOST_SYSTEM_USE_UTF8=1")
        if self.options.segmented_stacks:
            flags.extend(["segmented-stacks=on",
                          "define=BOOST_USE_SEGMENTED_STACKS=1",
                          "define=BOOST_USE_UCONTEXT=1"])
        flags.append("pch=on" if self.options.pch else "pch=off")

        if is_apple_os(self):
            apple_min_version_flag = AutotoolsToolchain(self).apple_min_version_flag
            if apple_min_version_flag:
                cxx_flags.append(apple_min_version_flag)
                link_flags.append(apple_min_version_flag)
            os_subsystem = self.settings.get_safe("os.subsystem")
            if os_subsystem == "catalyst":
                cxx_flags.append("--target=arm64-apple-ios-macabi")
                link_flags.append("--target=arm64-apple-ios-macabi")

        if self.settings.os == "iOS":
            if self.options.multithreading:
                cxx_flags.append("-DBOOST_SP_USE_SPINLOCK")

            if self.conf.get("tools.apple:enable_bitcode", check_type=bool):
                cxx_flags.append("-fembed-bitcode")
        if self._with_stacktrace_backtrace:
            flags.append(f"-sLIBBACKTRACE_PATH={self.dependencies['libbacktrace'].package_folder}")
        if self._stacktrace_from_exception_available and "x86" not in str(self.settings.arch):
            # https://github.com/boostorg/stacktrace/blob/boost-1.85.0/src/from_exception.cpp#L29
            # This feature is guarded by BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING, but that is only enabled on x86.
            flags.append("define=BOOST_STACKTRACE_LIBCXX_RUNTIME_MAY_CAUSE_MEMORY_LEAK=1")
        if self._with_iconv:
            flags.append(f"-sICONV_PATH={self.dependencies['libiconv'].package_folder}")
        if self._with_icu:
            flags.append(f"-sICU_PATH={self.dependencies['icu'].package_folder}")
            if not self.dependencies["icu"].options.shared:
                # Using ICU_OPTS to pass ICU system libraries is not possible due to Boost.Regex disallowing it.
                icu_system_libs = self.dependencies["icu"].cpp_info.aggregated_components().system_libs
                if is_msvc(self):
                    icu_ldflags = " ".join(f"{l}.lib" for l in icu_system_libs)
                else:
                    icu_ldflags = " ".join(f"-l{l}" for l in icu_system_libs)
                link_flags.append(icu_ldflags)

        link_flags = f'linkflags="{" ".join(link_flags)}"'
        flags.append(link_flags)

        if self.options.get_safe("addr2line_location"):
            cxx_flags.append(f"-DBOOST_STACKTRACE_ADDR2LINE_LOCATION={self.options.addr2line_location}")

        if not self.options.get_safe('without_cobalt', True) and \
            (self.settings.compiler == "gcc" and Version(self.settings.compiler.version) == "10"):
            cxx_flags.append("-fcoroutines")

        cxx_flags = f'cxxflags="{" ".join(cxx_flags)}"'
        flags.append(cxx_flags)

        if self.options.buildid:
            flags.append(f"--buildid={self.options.buildid}")
        if not self.options.without_python and self.options.python_buildid:
            flags.append(f"--python-buildid={self.options.python_buildid}")

        if self.options.extra_b2_flags:
            flags.extend(shlex.split(str(self.options.extra_b2_flags)))

        flags.extend([
            "install",
            f"--prefix={self.package_folder}",
            f"-j{build_jobs(self)}",
            "--abbreviate-paths",
            f"-d{self.options.debug_level}",
        ])
        return flags

    @property
    def _build_cross_flags(self):
        flags = []
        if not cross_building(self):
            return flags
        arch = self.settings.get_safe("arch")
        self.output.info("Cross building, detecting compiler...")

        if arch.startswith("arm"):
            if "hf" in arch:
                flags.append("-mfloat-abi=hard")
        elif self.settings.os == "Emscripten":
            pass
        elif arch in ["x86", "x86_64"]:
            pass
        elif arch.startswith("ppc"):
            pass
        elif arch.startswith("mips"):
            pass
        else:
            self.output.warning(f"Unable to detect the appropriate ABI for {arch} architecture.")
        self.output.info(f"Cross building flags: {flags}")

        return flags

    @property
    def _ar(self):
        ar = VirtualBuildEnv(self).vars().get("AR")
        if ar:
            return ar
        if is_apple_os(self) and self.settings.compiler == "apple-clang":
            return XCRun(self).ar
        return None

    @property
    def _ranlib(self):
        ranlib = VirtualBuildEnv(self).vars().get("RANLIB")
        if ranlib:
            return ranlib
        if is_apple_os(self) and self.settings.compiler == "apple-clang":
            return XCRun(self).ranlib
        return None

    @property
    def _cxx(self):
        compilers_by_conf = self.conf.get("tools.build:compiler_executables", default={}, check_type=dict)
        cxx = compilers_by_conf.get("cpp") or VirtualBuildEnv(self).vars().get("CXX")
        if cxx:
            return cxx
        if is_apple_os(self) and self.settings.compiler == "apple-clang":
            return XCRun(self).cxx
        compiler_version = str(self.settings.compiler.version)
        major = compiler_version.split(".", maxsplit=1)[0]
        if self.settings.compiler == "gcc":
            return shutil.which(f"g++-{compiler_version}") or shutil.which(f"g++-{major}") or shutil.which("g++") or ""
        if self.settings.compiler == "clang":
            return shutil.which(f"clang++-{compiler_version}") or shutil.which(f"clang++-{major}") or shutil.which("clang++") or ""
        return ""

    def _create_user_config_jam(self, folder):
        self.output.warning("Patching user-config.jam")

        def create_library_config(deps_name, name):
            aggregated_cpp_info = self.dependencies[deps_name].cpp_info.aggregated_components()
            if len(aggregated_cpp_info.libs) == 0:
                return ""

            includedir = aggregated_cpp_info.includedirs[0].replace("\\", "/")
            includedir = f"\"{includedir}\""
            libdir = aggregated_cpp_info.libdirs[0].replace("\\", "/")
            libdir = f"\"{libdir}\""
            lib = aggregated_cpp_info.libs[0]
            version = self.dependencies[deps_name].ref.version
            return f"\nusing {name} : {version} : " \
                   f"<include>{includedir} " \
                   f"<search>{libdir} " \
                   f"<name>{lib} ;"

        contents = ""

        if self._with_zlib:
            contents += create_library_config("zlib", "zlib")
        if self._with_bzip2:
            contents += create_library_config("bzip2", "bzip2")
        if self._with_lzma:
            contents += create_library_config("xz_utils", "lzma")
        if self._with_zstd:
            contents += create_library_config("zstd", "zstd")

        if not self.options.without_python:
            # https://www.boost.org/doc/libs/1_70_0/libs/python/doc/html/building/configuring_boost_build.html
            contents += f'\nusing python : {self._python_version} : "{self._python_executable}" : "{self._python_includes}" : "{self._python_library_dir}" ;'

        if not self.options.without_mpi:
            # https://www.boost.org/doc/libs/1_72_0/doc/html/mpi/getting_started.html
            contents += "\nusing mpi ;"

        # Specify here the toolset with the binary if present if don't empty parameter :
        contents += f'\nusing "{self._toolset}" : {self._toolset_version} : '

        cxx_fwd_slahes = self._cxx.replace("\\", "/")
        if cxx_fwd_slahes:
            contents += f" \"{cxx_fwd_slahes}\""

        if is_apple_os(self):
            if self.settings.compiler == "apple-clang":
                contents += f" -isysroot {XCRun(self).sdk_path}"
            if self.settings.get_safe("arch"):
                contents += f" -arch {to_apple_arch(self)}"

        contents += " : \n"
        if self._ar:
            ar_path = self._ar.replace("\\", "/")
            contents += f'<archiver>"{ar_path}" '
        if self._ranlib:
            ranlib_path = self._ranlib.replace("\\", "/")
            contents += f'<ranlib>"{ranlib_path}" '
        cxxflags = " ".join(self.conf.get("tools.build:cxxflags", default=[], check_type=list)) + " "
        cflags = " ".join(self.conf.get("tools.build:cflags", default=[], check_type=list)) + " "
        buildenv_vars = VirtualBuildEnv(self).vars()
        cppflags = buildenv_vars.get("CPPFLAGS", "") + " "
        ldflags = " ".join(self.conf.get("tools.build:sharedlinkflags", default=[], check_type=list)) + " "
        asflags = buildenv_vars.get("ASFLAGS", "") + " "

        sysroot = self.conf.get("tools.build:sysroot")
        if sysroot and not is_msvc(self):
            sysroot = sysroot.replace("\\", "/")
            sysroot = f'"{sysroot}"' if ' ' in sysroot else sysroot
            cppflags += f"--sysroot={sysroot} "
            ldflags += f"--sysroot={sysroot} "

        if self._with_stacktrace_backtrace:
            backtrace_aggregated_cpp_info = self.dependencies["libbacktrace"].cpp_info.aggregated_components()
            cppflags += " ".join(f"-I{p}" for p in backtrace_aggregated_cpp_info.includedirs) + " "
            ldflags += " ".join(f"-L{p}" for p in backtrace_aggregated_cpp_info.libdirs) + " "

        if cxxflags.strip():
            contents += f'<cxxflags>"{cxxflags.strip()}" '
        if cflags.strip():
            contents += f'<cflags>"{cflags.strip()}" '
        if cppflags.strip():
            contents += f'<compileflags>"{cppflags.strip()}" '
        if ldflags.strip():
            contents += f'<linkflags>"{ldflags.strip()}" '
        if asflags.strip():
            contents += f'<asmflags>"{asflags.strip()}" '

        if self._is_apple_embedded_platform:
            contents += f'<target-os>"{self._b2_os}" '

        contents += " ;"

        self.output.warning(contents)
        filename = f"{folder}/user-config.jam"
        save(self, filename, contents)

    @property
    def _toolset_version(self):
        toolset = MSBuildToolchain(self).toolset
        if toolset:
            match = re.match(r"v(\d+)(\d)$", toolset)
            if match:
                return f"{match.group(1)}.{match.group(2)}"
        return ""

    @property
    def _toolset(self):
        if is_msvc(self):
            return "clang-win" if self.settings.compiler.get_safe("toolset") == "ClangCL" else "msvc"
        if self.settings.os == "Windows" and self.settings.compiler == "clang":
            return "clang-win"
        if self.settings.os == "Emscripten" and self.settings.compiler == "clang":
            return "emscripten"
        if self.settings.compiler == "gcc" and is_apple_os(self):
            return "darwin"
        if self.settings.compiler == "apple-clang":
            return "clang-darwin"
        if self.settings.os == "Android" and self.settings.compiler == "clang":
            return "clang-linux"
        if self.settings.compiler in ["clang", "gcc"]:
            return str(self.settings.compiler)
        if self.settings.compiler == "sun-cc":
            return "sunpro"
        if "intel" in str(self.settings.compiler):
            return {
                "Macos": "intel-darwin",
                "Windows": "intel-win",
                "Linux": "intel-linux",
            }[str(self.settings.os)]

        return str(self.settings.compiler)

    @property
    def _toolset_tag(self):
        # compiler       | compiler.version | os          | toolset_tag    | remark
        # ---------------+------------------+-------------+----------------+-----------------------------
        # apple-clang    | 12               | Macos       | darwin12       |
        # clang          | 12               | Macos       | clang-darwin12 |
        # gcc            | 11               | Linux       | gcc8           |
        # gcc            | 8                | Windows     | mgw8           |
        # Visual Studio  | 17               | Windows     | vc142          | depends on compiler.toolset
        compiler = {
            "apple-clang": "",
            "Visual Studio": "vc",
            "msvc": "vc",
        }.get(str(self.settings.compiler), str(self.settings.compiler))
        if (self.settings.compiler, self.settings.os) == ("gcc", "Windows"):
            compiler = "mgw"
        os_ = ""
        if self.settings.os == "Macos":
            os_ = "darwin"
        if is_msvc(self):
            toolset_version = self._toolset_version.replace(".", "")
        else:
            toolset_version = str(Version(self.settings.compiler.version).major)

        toolset_parts = [compiler, os_]
        toolset_tag = "-".join(part for part in toolset_parts if part) + toolset_version
        return toolset_tag

    ####################################################################

    def package(self):
        # This stage/lib is in source_folder... Face palm, looks like it builds in build but then
        # copy to source with the good lib name
        copy(self, "LICENSE_1_0.txt", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
        rmdir(self, os.path.join(self.package_folder, "lib", "cmake"))
        if self.options.header_only:
            copy(self, "*", src=os.path.join(self.source_folder, "boost"),
                            dst=os.path.join(self.package_folder, "include", "boost"))

        if self.settings.os == "Emscripten" and not self.options.header_only:
            self._create_emscripten_libs()

        if is_msvc(self) and self._shared:
            # Some boost releases contain both static and shared variants of some libraries (if shared=True)
            all_libs = set(collect_libs(self, "lib"))
            static_libs = set(l for l in all_libs if l.startswith("lib"))
            shared_libs = all_libs.difference(static_libs)
            static_libs = set(l[3:] for l in static_libs)
            common_libs = static_libs.intersection(shared_libs)
            for common_lib in common_libs:
                common_lib_fullname = f"lib{common_lib}.lib"
                self.output.info(f'Unlinking static duplicate library: {os.path.join(self.package_folder, "lib", common_lib_fullname)}')
                os.unlink(os.path.join(self.package_folder, "lib", common_lib_fullname))

        dll_pdbs = glob.glob(os.path.join(self.package_folder, "lib", "*.dll")) + \
                    glob.glob(os.path.join(self.package_folder, "lib", "*.pdb"))
        if dll_pdbs:
            mkdir(self, os.path.join(self.package_folder, "bin"))
            for bin_file in dll_pdbs:
                rename(self, bin_file, os.path.join(self.package_folder, "bin", os.path.basename(bin_file)))

        rm(self, "*.pdb", os.path.join(self.package_folder, "bin"))

    def _create_emscripten_libs(self):
        # Boost Build doesn't create the libraries, but it gets close,
        # leaving .bc files where the libraries would be.
        staged_libs = os.path.join(
            self.package_folder, "lib"
        )
        if not os.path.exists(staged_libs):
            self.output.warning(f"Lib folder doesn't exist, can't collect libraries: {staged_libs}")
            return
        for bc_file in os.listdir(staged_libs):
            if bc_file.startswith("lib") and bc_file.endswith(".bc"):
                a_file = bc_file[:-3] + ".a"
                cmd = f"emar q {os.path.join(staged_libs, a_file)} {os.path.join(staged_libs, bc_file)}"
                self.output.info(cmd)
                self.run(cmd)

    @staticmethod
    def _option_to_conan_requirement(name):
        return {
            "lzma": "xz_utils",
            "iconv": "libiconv",
            "python": None,  # FIXME: change to cpython when it becomes available
        }.get(name, name)

    def package_info(self):
        self.env_info.BOOST_ROOT = self.package_folder

        self.cpp_info.set_property("cmake_file_name", "Boost")
        self.cpp_info.filenames["cmake_find_package"] = "Boost"
        self.cpp_info.filenames["cmake_find_package_multi"] = "Boost"
        self.cpp_info.names["cmake_find_package"] = "Boost"
        self.cpp_info.names["cmake_find_package_multi"] = "Boost"

        # - Use 'headers' component for all includes + defines
        # - Use '_libboost' component to attach extra system_libs, ...

        self.cpp_info.components["headers"].libs = []
        self.cpp_info.components["headers"].libdirs = []
        self.cpp_info.components["headers"].set_property("cmake_target_name", "Boost::headers")
        self.cpp_info.components["headers"].names["cmake_find_package"] = "headers"
        self.cpp_info.components["headers"].names["cmake_find_package_multi"] = "headers"
        self.cpp_info.components["headers"].names["pkg_config"] = "boost"

        if self.options.system_no_deprecated:
            self.cpp_info.components["headers"].defines.append("BOOST_SYSTEM_NO_DEPRECATED")

        if self.options.asio_no_deprecated:
            self.cpp_info.components["headers"].defines.append("BOOST_ASIO_NO_DEPRECATED")

        if self.options.filesystem_no_deprecated:
            self.cpp_info.components["headers"].defines.append("BOOST_FILESYSTEM_NO_DEPRECATED")

        if self.options.filesystem_use_std_fs:
            self.cpp_info.components["headers"].defines.append("BOOST_DLL_USE_STD_FS")

        if self.options.filesystem_version:
            self.cpp_info.components["headers"].defines.append(f"BOOST_FILESYSTEM_VERSION={self.options.filesystem_version}")

        if self.options.segmented_stacks:
            self.cpp_info.components["headers"].defines.extend(["BOOST_USE_SEGMENTED_STACKS", "BOOST_USE_UCONTEXT"])

        if self.options.system_use_utf8:
            self.cpp_info.components["headers"].defines.append("BOOST_SYSTEM_USE_UTF8")

        if self.options.buildid:
            # If you built Boost using the --buildid option then set this macro to the same value
            # as you passed to bjam.
            # For example if you built using bjam address-model=64 --buildid=amd64 then compile your code with
            # -DBOOST_LIB_BUILDID=amd64 to ensure the correct libraries are selected at link time.
            self.cpp_info.components["headers"].defines.append(f"BOOST_LIB_BUILDID={self.options.buildid}")

        if not self.options.header_only:
            if self.options.error_code_header_only:
                self.cpp_info.components["headers"].defines.append("BOOST_ERROR_CODE_HEADER_ONLY")

        if self.options.layout == "versioned":
            version = Version(self.version)
            self.cpp_info.components["headers"].includedirs.append(os.path.join("include", f"boost-{version.major}_{version.minor}"))

        # Boost::boost is an alias of Boost::headers
        self.cpp_info.components["_boost_cmake"].requires = ["headers"]
        self.cpp_info.components["_boost_cmake"].set_property("cmake_target_name", "Boost::boost")
        self.cpp_info.components["_boost_cmake"].names["cmake_find_package"] = "boost"
        self.cpp_info.components["_boost_cmake"].names["cmake_find_package_multi"] = "boost"
        if self.options.header_only:
            self.cpp_info.components["_boost_cmake"].libdirs = []

        if not self.options.header_only:
            self.cpp_info.components["_libboost"].requires = ["headers"]

            self.cpp_info.components["diagnostic_definitions"].libs = []
            self.cpp_info.components["diagnostic_definitions"].set_property("cmake_target_name", "Boost::diagnostic_definitions")
            self.cpp_info.components["diagnostic_definitions"].names["cmake_find_package"] = "diagnostic_definitions"
            self.cpp_info.components["diagnostic_definitions"].names["cmake_find_package_multi"] = "diagnostic_definitions"
            self.cpp_info.components["diagnostic_definitions"].names["pkg_config"] = "boost_diagnostic_definitions"  # FIXME: disable on pkg_config
            # I would assume headers also need the define BOOST_LIB_DIAGNOSTIC, as a header can trigger an autolink,
            # and this definition triggers a print out of the library selected.  See notes below on autolink and headers.
            self.cpp_info.components["headers"].requires.append("diagnostic_definitions")
            if self.options.diagnostic_definitions:
                self.cpp_info.components["diagnostic_definitions"].defines = ["BOOST_LIB_DIAGNOSTIC"]

            self.cpp_info.components["disable_autolinking"].libs = []
            self.cpp_info.components["disable_autolinking"].set_property("cmake_target_name", "Boost::disable_autolinking")
            self.cpp_info.components["disable_autolinking"].names["cmake_find_package"] = "disable_autolinking"
            self.cpp_info.components["disable_autolinking"].names["cmake_find_package_multi"] = "disable_autolinking"
            self.cpp_info.components["disable_autolinking"].names["pkg_config"] = "boost_disable_autolinking"  # FIXME: disable on pkg_config

            # Even headers needs to know the flags for disabling autolinking ...
            # magic_autolink is an option in the recipe, so if a consumer wants this version of boost,
            # then they should not get autolinking.
            # Note that autolinking can sneak in just by some file #including a header with (eg) boost/atomic.hpp,
            # even if it doesn't use any part that requires linking with libboost_atomic in order to compile.
            # So a boost-header-only library that links to Boost::headers needs to see BOOST_ALL_NO_LIB
            # in order to avoid autolinking to libboost_atomic

            # This define is already imported into all of the _libboost libraries from this recipe anyway,
            # so it would be better to be consistent and ensure ANYTHING using boost (headers or libs) has consistent #defines.

            # Same applies for for BOOST_AUTO_LINK_{layout}:
            # consumer libs that use headers also need to know what is the layout/filename of the libraries.
            #
            # eg, if using the "tagged" naming scheme, and a header triggers an autolink,
            # then that header's autolink request had better be configured to request the "tagged" library name.
            # Otherwise, the linker will be looking for a (eg) "versioned" library name, and there will be a link error.

            # Note that "_libboost" requires "headers" so these defines will be applied to all the libraries too.
            self.cpp_info.components["headers"].requires.append("disable_autolinking")
            if is_msvc(self) or self._is_clang_cl:
                if self.options.magic_autolink:
                    if self.options.layout == "system":
                        self.cpp_info.components["headers"].defines.append("BOOST_AUTO_LINK_SYSTEM")
                    elif self.options.layout == "tagged":
                        self.cpp_info.components["headers"].defines.append("BOOST_AUTO_LINK_TAGGED")
                    self.output.info("Enabled magic autolinking (smart and magic decisions)")
                else:
                    # DISABLES AUTO LINKING! NO SMART AND MAGIC DECISIONS THANKS!
                    self.cpp_info.components["disable_autolinking"].defines = ["BOOST_ALL_NO_LIB"]
                    self.output.info("Disabled magic autolinking (smart and magic decisions)")

            self.cpp_info.components["dynamic_linking"].libs = []
            self.cpp_info.components["dynamic_linking"].set_property("cmake_target_name", "Boost::dynamic_linking")
            self.cpp_info.components["dynamic_linking"].names["cmake_find_package"] = "dynamic_linking"
            self.cpp_info.components["dynamic_linking"].names["cmake_find_package_multi"] = "dynamic_linking"
            self.cpp_info.components["dynamic_linking"].names["pkg_config"] = "boost_dynamic_linking"  # FIXME: disable on pkg_config
            # A library that only links to Boost::headers can be linked into another library that links a Boost::library,
            # so for this reasons, the header-only library should know the BOOST_ALL_DYN_LINK definition as it will likely
            # change some important part of the boost code and cause linking errors downstream.
            # This is in the same theme as the notes above, re autolinking.
            self.cpp_info.components["headers"].requires.append("dynamic_linking")
            if self._shared:
                # A Boost::dynamic_linking cmake target does only make sense for a shared boost package
                self.cpp_info.components["dynamic_linking"].defines = ["BOOST_ALL_DYN_LINK"]

            # https://www.boost.org/doc/libs/1_73_0/more/getting_started/windows.html#library-naming
            # libsuffix for MSVC:
            # - system: ""
            # - versioned: "-vc142-mt-d-x64-1_74"
            # - tagged: "-mt-d-x64"
            libsuffix_lut = {
                "system": "",
                "versioned": "{toolset}{threading}{abi}{arch}{version}",
                "tagged": "{threading}{abi}{arch}",
            }
            libsuffix_data = {
                "toolset": f"-{self._toolset_tag}",
                "threading": "-mt" if self.options.multithreading else "",
                "abi": "",
                "ach": "",
                "version": "",
            }
            if is_msvc(self):  # FIXME: mingw?
                # FIXME: add 'y' when using cpython cci package and when python is built in debug mode
                static_runtime_key = "s" if is_msvc_static_runtime(self) else ""
                debug_runtime_key = "g" if "d" in msvc_runtime_flag(self) else ""
                debug_key = "d" if self.settings.build_type == "Debug" else ""
                abi = static_runtime_key + debug_runtime_key + debug_key
                if abi:
                    libsuffix_data["abi"] = f"-{abi}"
            else:
                debug_tag = "d" if self.settings.build_type == "Debug" else ""
                abi = debug_tag
                if abi:
                    libsuffix_data["abi"] = f"-{abi}"

            if self._b2_architecture:
                libsuffix_data["arch"] = f"-{self._b2_architecture[0]}{self._b2_address_model}"
            version = Version(self.version)
            if not version.patch or version.patch == "0":
                libsuffix_data["version"] = f"-{version.major}_{version.minor}"
            else:
                libsuffix_data["version"] = f"-{version.major}_{version.minor}_{version.patch}"
            libsuffix = libsuffix_lut[str(self.options.layout)].format(**libsuffix_data)
            if libsuffix:
                self.output.info(f"Library layout suffix: {repr(libsuffix)}")

            libformatdata = {}
            if not self.options.without_python:
                pyversion = Version(self._python_version)
                libformatdata["py_major"] = pyversion.major
                libformatdata["py_minor"] = pyversion.minor

            def add_libprefix(n):
                """ On MSVC, static libraries are built with a 'lib' prefix. Some libraries do not support shared, so are always built as a static library. """
                libprefix = ""
                if is_msvc(self) and (not self._shared or n in self._dependencies["static_only"]):
                    libprefix = "lib"
                elif self._toolset == "clang-win":
                    libprefix = "lib"
                return libprefix + n

            all_detected_libraries = set(l[:-4] if l.endswith(".dll") else l for l in collect_libs(self))
            all_expected_libraries = set()
            incomplete_components = []

            def filter_transform_module_libraries(names):
                libs = []
                for name in names:
                    if name in ("boost_stacktrace_windbg", "boost_stacktrace_windbg_cached") and self.settings.os != "Windows":
                        continue
                    if name in ("boost_math_c99l", "boost_math_tr1l") and str(self.settings.arch).startswith("ppc"):
                        continue
                    if name in ("boost_stacktrace_addr2line", "boost_stacktrace_backtrace", "boost_stacktrace_basic") and self.settings.os == "Windows":
                        continue
                    if name == "boost_stacktrace_from_exception" and not self._stacktrace_from_exception_available:
                        continue
                    if name == "boost_stacktrace_addr2line" and not self._stacktrace_addr2line_available:
                        continue
                    if name == "boost_stacktrace_backtrace" and self.options.get_safe("with_stacktrace_backtrace") == False:
                        continue
                    if not self.options.get_safe("numa") and "_numa" in name:
                        continue
                    new_name = add_libprefix(name.format(**libformatdata)) + libsuffix
                    if self.options.namespace != 'boost':
                        new_name = new_name.replace("boost_", str(self.options.namespace) + "_")
                    if name.startswith("boost_python") or name.startswith("boost_numpy"):
                        if self.options.python_buildid:
                            new_name += f"-{self.options.python_buildid}"
                    if self.options.buildid:
                        new_name += f"-{self.options.buildid}"
                    libs.append(new_name)
                return libs

            for module in self._dependencies["dependencies"].keys():
                missing_depmodules = list(depmodule for depmodule in self._all_dependent_modules(module) if self.options.get_safe(f"without_{depmodule}", False))
                if missing_depmodules:
                    continue

                module_libraries = filter_transform_module_libraries(self._dependencies["libs"][module])

                # Don't create components for modules that should have libraries, but don't have (because of filter)
                if self._dependencies["libs"][module] and not module_libraries:
                    continue

                all_expected_libraries = all_expected_libraries.union(module_libraries)
                if set(module_libraries).difference(all_detected_libraries):
                    incomplete_components.append(module)

                # Starting v1.69.0 Boost.System is header-only. A stub library is
                # still built for compatibility, but linking to it is no longer
                # necessary.
                # https://www.boost.org/doc/libs/1_75_0/libs/system/doc/html/system.html#changes_in_boost_1_69
                if module == "system":
                    module_libraries = []

                self.cpp_info.components[module].libs = module_libraries

                self.cpp_info.components[module].requires = self._dependencies["dependencies"][module] + ["_libboost"]
                self.cpp_info.components[module].set_property("cmake_target_name", "Boost::" + module)
                self.cpp_info.components[module].names["cmake_find_package"] = module
                self.cpp_info.components[module].names["cmake_find_package_multi"] = module
                self.cpp_info.components[module].names["pkg_config"] = f"boost_{module}"

                # extract list of names of direct host dependencies to check for dependencies
                # of components that exist in other packages
                dependencies = [d.ref.name for d, _ in self.dependencies.direct_host.items()]

                for requirement in self._dependencies.get("requirements", {}).get(module, []):
                    if self.options.get_safe(requirement, None) == False:
                        continue
                    conan_requirement = self._option_to_conan_requirement(requirement)
                    if conan_requirement not in dependencies:
                        continue
                    if module == "locale" and requirement in ("icu", "iconv"):
                        if requirement == "icu" and not self._with_icu:
                            continue
                        if requirement == "iconv" and not self._with_iconv:
                            continue
                    self.cpp_info.components[module].requires.append(f"{conan_requirement}::{conan_requirement}")

            for incomplete_component in incomplete_components:
                self.output.warning(f"Boost component '{incomplete_component}' is missing libraries. Try building boost with '-o boost:without_{incomplete_component}'. (Option is not guaranteed to exist)")

            non_used = all_detected_libraries.difference(all_expected_libraries)
            if non_used:
                raise ConanException(f"These libraries were built, but were not used in any boost module: {non_used}")

            non_built = all_expected_libraries.difference(all_detected_libraries)
            if non_built:
                raise ConanException(f"These libraries were expected to be built, but were not built: {non_built}")

            if not self.options.without_stacktrace:
                if self.settings.os in ("Linux", "FreeBSD"):
                    self.cpp_info.components["stacktrace_basic"].system_libs.append("dl")
                    if self._stacktrace_addr2line_available:
                        self.cpp_info.components["stacktrace_addr2line"].system_libs.append("dl")
                    if self._with_stacktrace_backtrace:
                        self.cpp_info.components["stacktrace_backtrace"].system_libs.append("dl")
                    if self._stacktrace_from_exception_available:
                        self.cpp_info.components["stacktrace_from_exception"].system_libs.append("dl")

                if self._stacktrace_addr2line_available:
                    self.cpp_info.components["stacktrace_addr2line"].defines.extend([
                        f"BOOST_STACKTRACE_ADDR2LINE_LOCATION=\"{self.options.addr2line_location}\"",
                        "BOOST_STACKTRACE_USE_ADDR2LINE",
                    ])

                if self._with_stacktrace_backtrace:
                    self.cpp_info.components["stacktrace_backtrace"].defines.append("BOOST_STACKTRACE_USE_BACKTRACE")
                    self.cpp_info.components["stacktrace_backtrace"].requires.append("libbacktrace::libbacktrace")

                self.cpp_info.components["stacktrace_noop"].defines.append("BOOST_STACKTRACE_USE_NOOP")

                if self.settings.os == "Windows":
                    self.cpp_info.components["stacktrace_windbg"].defines.append("BOOST_STACKTRACE_USE_WINDBG")
                    self.cpp_info.components["stacktrace_windbg"].system_libs.extend(["ole32", "dbgeng"])
                    self.cpp_info.components["stacktrace_windbg_cached"].defines.append("BOOST_STACKTRACE_USE_WINDBG_CACHED")
                    self.cpp_info.components["stacktrace_windbg_cached"].system_libs.extend(["ole32", "dbgeng"])
                elif is_apple_os(self) or self.settings.os == "FreeBSD":
                    self.cpp_info.components["stacktrace"].defines.append("BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED")

            if not self.options.without_python:
                pyversion = Version(self._python_version)
                self.cpp_info.components[f"python{pyversion.major}{pyversion.minor}"].requires = ["python"]
                if not self._shared:
                    self.cpp_info.components["python"].defines.append("BOOST_PYTHON_STATIC_LIB")

                self.cpp_info.components[f"numpy{pyversion.major}{pyversion.minor}"].requires = ["numpy"]

            if is_msvc(self) or self._is_clang_cl:
                # https://github.com/conan-community/conan-boost/issues/127#issuecomment-404750974
                self.cpp_info.components["_libboost"].system_libs.append("bcrypt")
            elif self.settings.os == "Linux":
                # https://github.com/conan-community/community/issues/135
                self.cpp_info.components["_libboost"].system_libs.append("rt")
                if self.options.multithreading:
                    self.cpp_info.components["_libboost"].system_libs.append("pthread")
            elif self.settings.os == "Emscripten":
                if self.options.multithreading:
                    arch = str(self.settings.arch)
                    # https://emscripten.org/docs/porting/pthreads.html
                    # The documentation mentions that we should be using the "-s USE_PTHREADS=1"
                    # but it was causing problems with the target based configurations in conan
                    # So instead we are using the raw compiler flags (that are being activated
                    # from the aforementioned flag)
                    if arch.startswith("x86") or arch.startswith("wasm"):
                        self.cpp_info.components["_libboost"].cxxflags.append("-pthread")
                        self.cpp_info.components["_libboost"].sharedlinkflags.extend(["-pthread","--shared-memory"])
                        self.cpp_info.components["_libboost"].exelinkflags.extend(["-pthread","--shared-memory"])
            elif self.settings.os == "iOS":
                if self.options.multithreading:
                    # https://github.com/conan-io/conan-center-index/issues/3867
                    # runtime crashes occur when using the default platform-specific reference counter/atomic
                    # https://github.com/boostorg/filesystem/issues/147
                    # iOS should use spinlocks to avoid filesystem crashes
                    self.cpp_info.components["headers"].defines.append("BOOST_SP_USE_SPINLOCK")
                else:
                    self.cpp_info.components["headers"].defines.extend(["BOOST_AC_DISABLE_THREADS", "BOOST_SP_DISABLE_THREADS"])

            if not self.options.get_safe('without_cobalt', True) and \
                (self.settings.compiler == "gcc" and Version(self.settings.compiler.version) == "10"):
                self.cpp_info.components["cobalt"].cxxflags.append("-fcoroutines")

        #TODO: remove in the future, user_info deprecated in conan2, but kept for compatibility while recipe is cross-compatible.
        self.user_info.stacktrace_addr2line_available = self._stacktrace_addr2line_available
        self.conf_info.define("user.boost:stacktrace_addr2line_available", self._stacktrace_addr2line_available)
